﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using static AJWPFAdmin.Core.Crypto.RSA.AJRSA;

namespace AJWPFAdmin.Core.Crypto.RSA
{
    public class AJRSA
    {
        private static readonly string _defaultPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCn442LIMdkmsJ0d/9WkJcgcgokuklGXYbpKMS3DWfJObyaN6SqRtEDJ4JIWdN4KlT2LatjQ68KFKbM68AJ7G5tGL3RETnhTtG3n0vZFXWuSG3tn0GraxpjSSQ3i+cU8/A1qXe/7IQxdwqvPay1zQKKnPYdSNXMxA623vH7ddvKVQIDAQAB";

        private static readonly string _defaultPrivateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKfjjYsgx2SawnR3/1aQlyByCiS6SUZdhukoxLcNZ8k5vJo3pKpG0QMngkhZ03gqVPYtq2NDrwoUpszrwAnsbm0YvdEROeFO0befS9kVda5Ibe2fQatrGmNJJDeL5xTz8DWpd7/shDF3Cq89rLXNAoqc9h1I1czEDrbe8ft128pVAgMBAAECgYALcvXr2zolxvUEVsuTpmwb8xL0grys/wNMgrJ5jkE55IVTWgCReR9XUPouP2vvqaleGWqy7AJzlEl0t44CzzwPecYMMn8iHcBzHJYfPXqHx/c2cf1R/BaEShuDOC+JVliHviuJbzyrdlnOH6BBD93fpSdHx9CyZ/fMJG2SIY9/iQJBANAOQDmN2lcfZcnze2UN542j3Z1pLqcTs9A+ofzFUGwMXBSIFr0WBk/QZv9S+fZSfszx6GVORIMTmg1WdLivZt0CQQDOk8ID2++L59PJN43mJb7wk2wWiYrRqKNhBbMiDjgvvkTbN6f+zsZTC8z68/NAGfiX7dBT5n2ExlnhiJeAD+3ZAkEAz2bVaUpadO0suEQLJzLUOGM5Abkfs5g1cQtuyZ8cy4kKpcEAAeCZzCiw9CeQXJfiJYWlKI/8RjpdIJyl4JGh1QJATQiOpg/DEYH+2R9NXBChVmczPGhICwz+9/Q/K6jvzOYAGVt02zFSha913fNQt4u3xzgYouDl17h71f2wuUG3aQJAEaG2/wdhLJrNSdtfkluh3cxM9lIjqxoWbvv36VdHEo5Tua7h2N8XaR2jeg6LhUHxbkRyrJBIhVNj7C789Krwlw==";

        public enum KeyFormat
        {
            PKCS1,
            PKCS8,
        }

        public static string Encrypt(string content, string publicKeyPem)
        {
            //假设私钥长度为1024， 1024/8-11=117。
            //如果明文的长度小于117，直接全加密，然后转base64。(data.Length <= maxBlockSize)
            //如果明文长度大于117，则每117分一段加密，写入到另一个Stream中，最后转base64。while (blockSize > 0)                 
            if (string.IsNullOrWhiteSpace(publicKeyPem))
            {
                publicKeyPem = _defaultPublicKey;
            }
            //转为纯字符串，不带格式
            publicKeyPem = publicKeyPem.Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();

            using var rsa = System.Security.Cryptography.RSA.Create();
            rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicKeyPem), out _);

            byte[] data = Encoding.UTF8.GetBytes(content);
            int maxBlockSize = rsa.KeySize / 8 - 11; //加密块最大长度限制
            if (data.Length <= maxBlockSize)
            {
                byte[] cipherbytes = rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1);
                return Convert.ToBase64String(cipherbytes);
            }
            using var plaiStream = new MemoryStream(data);
            using var crypStream = new MemoryStream();
            byte[] buffer = new byte[maxBlockSize];
            int blockSize = plaiStream.Read(buffer, 0, maxBlockSize);
            while (blockSize > 0)
            {
                byte[] toEncrypt = new byte[blockSize];
                Array.Copy(buffer, 0, toEncrypt, 0, blockSize);
                byte[] cryptograph = rsa.Encrypt(toEncrypt, RSAEncryptionPadding.Pkcs1);
                crypStream.Write(cryptograph, 0, cryptograph.Length);
                blockSize = plaiStream.Read(buffer, 0, maxBlockSize);
            }

            return Convert.ToBase64String(crypStream.ToArray(), Base64FormattingOptions.None);
        }

        public static string Decrypt(string content, string privateKeyPem, KeyFormat keyFormat)
        {
            //假设私钥长度为1024， 1024/8 =128。
            //如果明文的长度小于 128，直接全解密。(data.Length <= maxBlockSize)
            //如果明文长度大于 128，则每 128 分一段解密，写入到另一个Stream中，最后 GetString。while (blockSize > 0)

            if (string.IsNullOrWhiteSpace(privateKeyPem))
            {
                privateKeyPem = _defaultPrivateKey;
            }

            //转为纯字符串，不带格式
            privateKeyPem = privateKeyPem.Replace("-----BEGIN RSA PRIVATE KEY-----", "").Replace("-----END RSA PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();
            privateKeyPem = privateKeyPem.Replace("-----BEGIN PRIVATE KEY-----", "").Replace("-----END PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();


            using var rsaCsp = System.Security.Cryptography.RSA.Create();
            if (keyFormat == KeyFormat.PKCS8)
                rsaCsp.ImportPkcs8PrivateKey(Convert.FromBase64String(privateKeyPem), out _);
            else if (keyFormat == KeyFormat.PKCS1)
                rsaCsp.ImportRSAPrivateKey(Convert.FromBase64String(privateKeyPem), out _);
            else
                throw new Exception("只支持PKCS8，PKCS1");

            byte[] data = Convert.FromBase64String(content);
            int maxBlockSize = rsaCsp.KeySize / 8; //解密块最大长度限制
            if (data.Length <= maxBlockSize)
            {
                byte[] cipherbytes = rsaCsp.Decrypt(data, RSAEncryptionPadding.Pkcs1);
                return Encoding.UTF8.GetString(cipherbytes);
            }
            using var crypStream = new MemoryStream(data);
            using var plaiStream = new MemoryStream();
            byte[] buffer = new byte[maxBlockSize];
            int blockSize = crypStream.Read(buffer, 0, maxBlockSize);
            while (blockSize > 0)
            {
                byte[] toDecrypt = new byte[blockSize];
                Array.Copy(buffer, 0, toDecrypt, 0, blockSize);
                byte[] cryptograph = rsaCsp.Decrypt(toDecrypt, RSAEncryptionPadding.Pkcs1);
                plaiStream.Write(cryptograph, 0, cryptograph.Length);
                blockSize = crypStream.Read(buffer, 0, maxBlockSize);
            }

            return Encoding.UTF8.GetString(plaiStream.ToArray());
        }

        public static string Sign(string content, string privateKeyPem, KeyFormat keyFormat)
        {
            //假设私钥长度为1024， 1024/8 =128。
            //如果明文的长度小于 128，直接全解密。(data.Length <= maxBlockSize)
            //如果明文长度大于 128，则每 128 分一段解密，写入到另一个Stream中，最后 GetString。while (blockSize > 0)
            //转为纯字符串，不带格式

            if (string.IsNullOrWhiteSpace(privateKeyPem))
            {
                privateKeyPem = _defaultPrivateKey;
            }

            privateKeyPem = privateKeyPem.Replace("-----BEGIN RSA PRIVATE KEY-----", "").Replace("-----END RSA PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();
            privateKeyPem = privateKeyPem.Replace("-----BEGIN PRIVATE KEY-----", "").Replace("-----END PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();


            using var rsaCsp = System.Security.Cryptography.RSA.Create();
            if (keyFormat == KeyFormat.PKCS8)
                rsaCsp.ImportPkcs8PrivateKey(Convert.FromBase64String(privateKeyPem), out _);
            else if (keyFormat == KeyFormat.PKCS1)
                rsaCsp.ImportRSAPrivateKey(Convert.FromBase64String(privateKeyPem), out _);
            else
                throw new Exception("只支持PKCS8，PKCS1");

            byte[] data = Encoding.UTF8.GetBytes(content);

            var array = rsaCsp.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

            return Convert.ToBase64String(array);
        }

        public static byte[] Sign(byte[] content, string privateKeyPem, KeyFormat keyFormat)
        {
            //假设私钥长度为1024， 1024/8 =128。
            //如果明文的长度小于 128，直接全解密。(data.Length <= maxBlockSize)
            //如果明文长度大于 128，则每 128 分一段解密，写入到另一个Stream中，最后 GetString。while (blockSize > 0)
            //转为纯字符串，不带格式

            if (string.IsNullOrWhiteSpace(privateKeyPem))
            {
                privateKeyPem = _defaultPrivateKey;
            }

            privateKeyPem = privateKeyPem.Replace("-----BEGIN RSA PRIVATE KEY-----", "").Replace("-----END RSA PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();
            privateKeyPem = privateKeyPem.Replace("-----BEGIN PRIVATE KEY-----", "").Replace("-----END PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();


            using var rsaCsp = System.Security.Cryptography.RSA.Create();
            if (keyFormat == KeyFormat.PKCS8)
                rsaCsp.ImportPkcs8PrivateKey(Convert.FromBase64String(privateKeyPem), out _);
            else if (keyFormat == KeyFormat.PKCS1)
                rsaCsp.ImportRSAPrivateKey(Convert.FromBase64String(privateKeyPem), out _);
            else
                throw new Exception("只支持PKCS8，PKCS1");

            var array = rsaCsp.SignData(content, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

            return array;
        }

        public static bool VerifySign(string content,string value,  string publicKeyPem)
        {
            //假设私钥长度为1024， 1024/8-11=117。
            //如果明文的长度小于117，直接全加密，然后转base64。(data.Length <= maxBlockSize)
            //如果明文长度大于117，则每117分一段加密，写入到另一个Stream中，最后转base64。while (blockSize > 0)                 
            if (string.IsNullOrWhiteSpace(publicKeyPem))
            {
                publicKeyPem = _defaultPublicKey;
            }
            //转为纯字符串，不带格式
            publicKeyPem = publicKeyPem.Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();

            using var rsa = System.Security.Cryptography.RSA.Create();
            rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicKeyPem), out _);

            byte[] data = Convert.FromBase64String(content);
            var valueArray = Encoding.UTF8.GetBytes(value);

            return rsa.VerifyData(valueArray, data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        }

        public static bool VerifySign(byte[] content, byte[] value, string publicKeyPem)
        {
            //假设私钥长度为1024， 1024/8-11=117。
            //如果明文的长度小于117，直接全加密，然后转base64。(data.Length <= maxBlockSize)
            //如果明文长度大于117，则每117分一段加密，写入到另一个Stream中，最后转base64。while (blockSize > 0)                 
            if (string.IsNullOrWhiteSpace(publicKeyPem))
            {
                publicKeyPem = _defaultPublicKey;
            }
            //转为纯字符串，不带格式
            publicKeyPem = publicKeyPem.Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();

            using var rsa = System.Security.Cryptography.RSA.Create();
            rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicKeyPem), out _);


            return rsa.VerifyData(value, content, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        }
    }
}
